package cn.xu.tool.big.a.compments.transaction;

import cn.xu.tool.big.a.compments.strategy.TradingStrategy;
import cn.xu.tool.big.a.dto.KLine;
import cn.xu.tool.big.a.dto.TotalReturn;
import cn.xu.tool.big.a.dto.TradeRecord;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 类名: TransactionTest
 * 描述: TODO
 * 作者: ClarkXu clark2021@qq.com
 * 日期: 2025/7/16
 */
public class TransactionHalfCp {

    /**
     * @param kLines
     * @param initialCapital
     * @param initTotalPosition
     * @param initialTotalCost
     * @param intLastBuyPrice
     * @param commissionRate
     * @param totalReturn
     * @param shortPeriod
     * @param longPeriod
     * @param buyDiscount
     * @param sellDiscount
     * @param maxSellDiscount 最大止盈点
     * @param stopLossDiscount 止损/止盈点
     * @param t0 是否T+0交易
     * @param stopLossHalfSell 到达止损/止盈点是否以半仓卖出
     * @return
     * @throws Exception
     */
    public static List<TradeRecord> doTransactionBt(List<KLine> kLines,
                                                    double initialCapital,
                                                    int initTotalPosition,
                                                    double initialTotalCost,
                                                    double intLastBuyPrice,
                                                    double commissionRate,
                                                    TotalReturn totalReturn,
                                                    int shortPeriod,
                                                    int longPeriod,
                                                    double buyDiscount,
                                                    double sellDiscount,
                                                    double maxSellDiscount,
                                                    double stopLossDiscount,
                                                    boolean t0,
                                                    boolean stopLossHalfSell) throws Exception {  // 添加t0参数
        // 3. 生成交易信号
        int[] signals = TradingStrategy.generateSignals(kLines, shortPeriod, longPeriod);

        // 4. 回测
        return backtest(kLines, signals, initialCapital, initTotalPosition, initialTotalCost,
                intLastBuyPrice, commissionRate, totalReturn, false, buyDiscount, sellDiscount, maxSellDiscount, stopLossDiscount, t0, stopLossHalfSell);  // 传递t0参数
    }

    public static List<TradeRecord> backtest(List<KLine> kLines, int[] signals,
                                             double initialCapital, int initTotalPosition, double initialTotalCost,
                                             double intLastBuyPrice, double commissionRate,
                                             TotalReturn totalReturn, boolean allSell,
                                             double buyDiscount, double sellDiscount, double maxSellDiscount, double stopLossDiscount,
                                             boolean t0, boolean stopLossHalfSell) {
        double capital = initialCapital;
        int totalPosition = initTotalPosition;
        double totalCost = initialTotalCost;
        double lastBuyPrice = intLastBuyPrice;
        double lastSellPrice = 0;
        boolean isFirstBuy = true;
        int tradeCount = 0;
        List<TradeRecord> records = new ArrayList<>();
        System.out.println("日期\t\t\t\t\t操作\t价格\t\t成交量\t仓位\t\t现金\t\t总资产");

        // 添加变量记录当日买入数量
        int todayBuy = 0;

        for (int i = 0; i < kLines.size(); i++) {
            KLine kLine = kLines.get(i);
            int signal = signals[i];
            double price = kLine.getClose();

            // 重置当日买入数量
            todayBuy = 0;

            // 买入逻辑
            if ((signal == 1 && capital > 0)||(capital > 0 && isFirstBuy)) {
                boolean canBuy = isFirstBuy || (price <= lastSellPrice * buyDiscount);
                if (canBuy) {
                    double commission = commissionRate * capital;
                    if (commission < 5) {
                        commission = 5;
                    }
                    double amount = capital - commission;
                    int tmpPosition = (int) (amount / price);
                    tmpPosition = tmpPosition - tmpPosition % 100;
                    if (tmpPosition >= 100) {
                        totalPosition += tmpPosition;
                        capital -= tmpPosition * price;
                        lastBuyPrice = price * (1 + commissionRate);
                        tradeCount++;
                        isFirstBuy = false;

                        // 记录当日买入数量
                        todayBuy = tmpPosition;

                        System.out.printf("%s\t买入\t%.3f\t%d\t%d\t%.2f\t%.2f\n",
                                kLine.getDate(), price, tmpPosition, totalPosition, capital, capital + totalPosition * price);
                        records.add(TradeRecord.create(kLine.getDate(), "买入", price, totalPosition, capital,
                                capital + totalPosition * price, tmpPosition));
                        totalCost += tmpPosition * price;
                    }
                } else if (!isFirstBuy) {
                    double total = capital + totalPosition * price;
                    records.add(TradeRecord.create(kLine.getDate(), "买入跳过(价差不足)", price,
                            totalPosition, capital, total, 0));
                }
            }

            // 卖出逻辑
            if (totalPosition > 0) {
                double averageCost = totalCost / totalPosition;
                double minSellPrice = averageCost * sellDiscount;
                double maxSellPrice = averageCost * maxSellDiscount;
                double stopLossPrice = averageCost * stopLossDiscount;

                // 计算可卖出数量（考虑T+1规则）
                int availableToSell = t0 ? totalPosition : (totalPosition - todayBuy);

                if ((signal == -1 && price >= minSellPrice) && availableToSell > 0) {
                    // 计算实际可卖出数量（整百）
                    int sellVolume = availableToSell - availableToSell % 100;
                    if (sellVolume <= 0) {
                        double total = capital + totalPosition * price;
                        records.add(TradeRecord.create(kLine.getDate(), "持仓（可卖数量不足）", price,
                                totalPosition, capital, total, 0));
                        continue;
                    }

                    double commission = commissionRate * sellVolume * price;
                    if (commission < 5) {
                        commission = 5;
                    }
                    double sellValue = sellVolume * price - commission;
                    double costPerShare = totalCost / totalPosition;
                    double buyCost = sellVolume * costPerShare;
                    double profit = sellValue - buyCost;
                    double tmpReturnRate = (buyCost != 0) ? (profit / buyCost) * 100 : 0;

                    capital += sellValue;
                    lastSellPrice = price;
                    totalPosition -= sellVolume;
                    tradeCount++;

                    totalCost -= buyCost;

                    double totalAsset = capital + totalPosition * price;

                    System.out.print(String.format("%s\t卖出\t%.3f\t%d\t%d\t%.2f\t%.2f\t收益率(%.2f",
                            kLine.getDate(), price, sellVolume, totalPosition, capital, totalAsset, tmpReturnRate) + "%)\n");
                    records.add(TradeRecord.create(kLine.getDate(), "卖出", price,
                            totalPosition, capital, totalAsset, sellVolume));
                } else if ((price > maxSellPrice || price <= stopLossPrice) && availableToSell > 0 && stopLossHalfSell) {
                    // 计算半仓可卖出数量
                    int sellVolume;
                    if (totalPosition == 100) {
                        sellVolume = totalPosition;
                    } else {
                        sellVolume = (availableToSell / 2) - (availableToSell / 2) % 100;
                    }

                    if (sellVolume <= 0) {
                        double total = capital + totalPosition * price;
                        records.add(TradeRecord.create(kLine.getDate(), "持仓（可卖数量不足）", price,
                                totalPosition, capital, total, 0));
                        continue;
                    }

                    double commission = commissionRate * sellVolume * price;
                    if (commission < 5) commission = 5;

                    double sellValue = sellVolume * price - commission;
                    double costPerShare = totalCost / totalPosition;
                    double buyCost = sellVolume * costPerShare;
                    double profit = sellValue - buyCost;
                    double tmpReturnRate = (buyCost != 0) ? (profit / buyCost) * 100 : 0;

                    capital += sellValue;
                    lastSellPrice = price;
                    totalPosition -= sellVolume;
                    tradeCount++;

                    totalCost -= buyCost;
                    double totalAsset = capital + totalPosition * price;

                    System.out.print(String.format("%s\t卖出(半仓)\t%.3f\t%d\t%d\t%.2f\t%.2f\t收益率(%.2f",
                            kLine.getDate(), price, sellVolume, totalPosition, capital, totalAsset, tmpReturnRate) + "%)\n");
                    records.add(TradeRecord.create(kLine.getDate(), "卖出", price,
                            totalPosition, capital, totalAsset, sellVolume));
                } else if (signal == -1) {
                    double total = capital + totalPosition * price;
                    records.add(TradeRecord.create(kLine.getDate(), "持仓（盈利未达标）", price,
                            totalPosition, capital, total, 0));
                }
            }

            // 无信号时记录持仓
            else if (totalPosition > 0) {
                double total = capital + totalPosition * price;
                records.add(TradeRecord.create(kLine.getDate(), "持仓", price, totalPosition, capital, total, 0));
            }
        }

        // 其余代码保持不变...
        boolean doAllSell = false;
        if (totalPosition > 0) {
            KLine last = kLines.get(kLines.size() - 1);
            double price = last.getClose();
            double value = totalPosition * price;
            double commission = commissionRate * value;
            if (commission < 5) {
                commission = 5;
            }

            capital += value - commission;
            if (allSell) {
                System.out.printf("%s\t强制平仓\t%.2f\t%d\t%d\t%.2f\t%.2f\n",
                        last.getDate(), price, totalPosition, totalPosition, capital, capital);
                records.add(TradeRecord.create(last.getDate(), "强制平仓", price, 0, capital, capital, totalPosition));
                totalPosition = 0;
                totalCost = 0;
                doAllSell = true;
            } else {
                calculateMinSell(totalCost, totalPosition, sellDiscount);
            }
        } else {
            calculateMaxBuy(lastSellPrice, buyDiscount);
        }
        if (initialCapital == 0) {
            initialCapital = initialTotalCost;
        }
        double finalReturn = capital - initialCapital;
        double returnRate = finalReturn / initialCapital * 100;
        System.out.println("期初股价: " + kLines.get(0).getClose());
        System.out.println("期末股价: " + kLines.get(kLines.size() - 1).getClose());
        System.out.println("初始资金: " + initialCapital);
        if (doAllSell || totalPosition == 0) {
            System.out.println("最终资金: " + capital);
        } else {
            System.out.println("期末仓位(股): " + totalPosition + " * 最新收盘价换算 -> 最终资金: " + capital + " 持仓成本: " + (totalCost / totalPosition));
        }
        System.out.println("总收益率: " + String.format("%.2f", returnRate) + "%");
        System.out.println("交易次数: " + tradeCount);
        totalReturn.setInitialCapital(initialCapital);
        totalReturn.setFinalCapital(capital);
        totalReturn.setReturnRate(String.format("%.2f", returnRate) + "%");
        totalReturn.setTradeCount(tradeCount);
        return records;
    }

    //计算下次卖出的最低价格（不低于持仓成本×sellDiscount）
    private static double calculateMinSell(double totalCost, int totalPosition, double sellDiscount) {
        if (totalPosition <= 0) return 0.0;

        double averageCost = totalCost / totalPosition;
        double minSellPrice = averageCost * sellDiscount;
        System.out.printf("【预测】下次卖出的最低价格：%.3f (持仓成本×%.3f)\n", minSellPrice, sellDiscount);
        return minSellPrice;
    }

    //计算下次买入的最高价格（不高于上次卖出价×buyDiscount）
    private static double calculateMaxBuy(double lastSellPrice, double buyDiscount) {
        if (lastSellPrice <= 0) return Double.MAX_VALUE;

        double maxBuyPrice = lastSellPrice * buyDiscount;
        System.out.printf("【预测】下次买入的最高价格：%.3f (前次卖出价×%.3f)\n", maxBuyPrice, buyDiscount);
        return maxBuyPrice;
    }

}
